﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Nsf.Samples.RetryActivitySample.Infrastructure.Common.Commands;
using Nsf.Samples.RetryActivitySample.Infrastructure.Common.FaultDetails;
using Nsf.Samples.RetryActivitySample.Infrastructure.InternalInterfaces;
using Nsf.Samples.RetryActivitySample.Infrastructure.ServiceBase;
using Nsf.Samples.RetryActivitySample.Service.Infrastructure;
using Nsf.Samples.RetryActivitySample.ServiceContract;

namespace Nsf.Samples.RetryActivitySample.Service
{
    [ServiceErrorsHandlerBehavior]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class WorkflowManager : ServiceBase, IWorkflowManager
    {
        #region InternalMethods

        protected List<RetryableActivityInfo> ConvertToDataContract(List<RetryableActivityErrorInfo> retryableActivityErrorInfos)
        {
            return (from retryableActivityErrorInfo in retryableActivityErrorInfos
                    select new RetryableActivityInfo()
                           {
                               ActivityDescription = retryableActivityErrorInfo.ActivityDescription,
                               ActivityName = retryableActivityErrorInfo.ActivityName,
                               ErrorMessage = retryableActivityErrorInfo.ErrorMessage,
                               QueueName = retryableActivityErrorInfo.QueueName,
                               WorkflowInstanceId = retryableActivityErrorInfo.WorkflowInstanceId
                           }).ToList();
        }

        protected RetryableWorkflowInfo ConvertToDataContract(RetryableWorkflowErrorInfo retryableWorkflowInfo)
        {
            return new RetryableWorkflowInfo()
                       {
                           WorkflowInstanceId = retryableWorkflowInfo.WorkflowInstanceId,
                           WorkflowType = retryableWorkflowInfo.WorkflowType,
                           RetryableActivities = ConvertToDataContract(retryableWorkflowInfo.RetryableActivities)
                       };
        }

        #endregion

        #region IWorkflowManager Members

        public Guid StartWorkflow(String workflowType)
        {
            if (String.IsNullOrEmpty(workflowType))
                ThrowParameterValidationException("workflowType", "0", "Не указан тип рабочего потока");

            var type = WorkflowTypesManager.GetWorkflowType(workflowType);
            return WorkflowRuntimeManager.Instance.StartWorkflow(type);
        }

        public RetryableWorkflowInfo GetRetryableWorkflow(Guid workflowInstanceId)
        {
            if (workflowInstanceId == Guid.Empty)
                ThrowParameterValidationException("workflowInstanceId", "0", "Не указан идентификатор рабочего потока");

            var retryableWorkflow = WorkflowRuntimeMonitor.Instance.GetRetryableWorkflow(workflowInstanceId);
            return ConvertToDataContract(retryableWorkflow);
        }

        public List<RetryableWorkflowInfo> GetRetryableWorkflows(String workflowType)
        {
            var type = String.IsNullOrEmpty(workflowType) ? null : WorkflowTypesManager.GetWorkflowType(workflowType);
            var retryableWorkflows = WorkflowRuntimeMonitor.Instance.GetRetryableWorkflows(type);
            return retryableWorkflows.Select(obj => ConvertToDataContract(obj)).ToList();
        }

        public void RetryActivity(RetryableActivityInfo retryableActivityInfo)
        {
            if (retryableActivityInfo == null)
                ThrowParameterValidationException("retryableActivityInfo", "0", "Не указана информация о перезапускаемом действии");
            if (retryableActivityInfo.WorkflowInstanceId == Guid.Empty)
                ThrowParameterValidationException("retryableActivityInfo.WorkflowInstanceId", "0", "Не указан идентификатор рабочего потока");
            if (retryableActivityInfo.QueueName == Guid.Empty)
                ThrowParameterValidationException("retryableActivityInfo.QueueName", "0", "Не указано название очереди");

            WorkflowRuntimeManager.Instance.PutIntoQueue(retryableActivityInfo.WorkflowInstanceId, retryableActivityInfo.QueueName, null);
        }

        public void RetryWorkflow(RetryableWorkflowInfo retryableWorkflowInfo)
        {
            if (retryableWorkflowInfo == null)
                ThrowParameterValidationException("retryableActivityInfo", "0", "Не указана информация о перезапускаемом действии");
            if (retryableWorkflowInfo.WorkflowInstanceId == Guid.Empty)
                ThrowParameterValidationException("retryableActivityInfo.WorkflowInstanceId", "0", "Не указан идентификатор рабочего потока");
            var validationFaultDetailsItems = (from retryableActivity in retryableWorkflowInfo.RetryableActivities
                                               where retryableActivity.QueueName == Guid.Empty
                                               select new ValidationFaultDetailsItem("0", String.Format("Для действия '{0}' не указано имя очереди", retryableActivity.ActivityName))
                                              ).ToList();
            ThrowObjectValidationException(validationFaultDetailsItems);

            foreach (var retryableActivity in retryableWorkflowInfo.RetryableActivities)
            {
                WorkflowRuntimeManager.Instance.PutIntoQueue(retryableWorkflowInfo.WorkflowInstanceId, retryableActivity.QueueName, null);
            }
        }

        public void SendCommandToWorkflow(Guid workflowInstanceId, CommandData commandData)
        {
            if (workflowInstanceId == Guid.Empty)
                ThrowParameterValidationException("workflowInstanceId", "0", "Не указан идентификатор рабочего потока");
            if (commandData == null)
                ThrowParameterValidationException("commandData", "0", "Не указана информация о передаваемой команде");

            WorkflowRuntimeManager.Instance.PutIntoQueue(workflowInstanceId, workflowInstanceId, commandData);
        }

        #endregion
    }
}
